#include "utils/shell_utils.hpp"
#include <cstdlib>
#include <cstring>
#include "nfd.h"
#include "utils/log.hpp"
#include "utils/os_utils.hpp"

#ifdef _MSC_VER
#define strdup _strdup
#endif

static std::vector<std::string> tokenize(const std::string& s, char c) {
    auto end = s.cend();
    auto start = end;

    std::vector<std::string> v;
    for( auto it = s.cbegin(); it != end; ++it ) {
        if( *it != c ) {
            if( start == end )
                start = it;
            continue;
        }
        if( start != end ) {
            v.emplace_back(start, it);
            start = end;
        }
    }
    if( start != end )
        v.emplace_back(start, end);
    return v;
}

static std::vector<nfdu8filteritem_t> ParseFilters(const std::string &filters)
{
    std::vector<nfdu8filteritem_t> result;
    if (!filters.empty())
    {
        auto filter_items = tokenize(filters, ';');
        for (auto const &x: filter_items)
        {
            auto left = x.find('{');
            auto right = x.find('}', left);
            auto name = x.substr(0, left);
            auto spec = x.substr(left+1, right-left-1);
            if (!name.empty() && !spec.empty()) {
                result.emplace_back(
                    nfdu8filteritem_t {
                        .name = strdup(name.c_str()),
                        .spec = strdup(spec.c_str())
                    }
                );
            }
        }
    }
    return result;
}

static void FiltersFree(const std::vector<nfdu8filteritem_t> &filters) {
    for (auto &x: filters) {
        free((void*)x.name);
        free((void*)x.spec);
    }
}

std::string ChooseFile(const std::string &filters)
{
    std::string strPath;

    auto const filter_list = ParseFilters(filters);

    nfdu8char_t *path;
    nfdresult_t res = NFD_OpenDialogU8(&path, filter_list.data(), (nfdfiltersize_t)filter_list.size(), nullptr);
    if (res == NFD_OKAY)
    {
        strPath = path;
        NFD_FreePathU8(path);
    }

    FiltersFree(filter_list);

    return strPath;
}

std::vector<std::string> ChooseFiles(const std::string &filters)
{
    std::vector<std::string> strPaths;
    auto const filter_list = ParseFilters(filters);

    const nfdpathset_t *paths = nullptr;
    nfdresult_t res = NFD_OpenDialogMultipleU8(&paths, filter_list.data(), (nfdfiltersize_t)filter_list.size(), nullptr);
    if (res == NFD_OKAY)
    {
        nfdpathsetsize_t count = 0;
        NFD_PathSet_GetCount(paths, &count);
        for (nfdpathsetsize_t i=0; i<count; ++i) {
            nfdu8char_t *path = nullptr;
            NFD_PathSet_GetPathU8(paths, i, &path);
            strPaths.emplace_back(path);
            NFD_PathSet_FreePathU8(path);
        }
        NFD_PathSet_Free(paths);
    }

    FiltersFree(filter_list);

    return strPaths;
}

std::string SaveFile(const std::string &name, const std::string &filters)
{
    std::string strPath;
    auto const filter_list = ParseFilters(filters);

    nfdu8char_t *path = nullptr;
    nfdresult_t res = NFD_SaveDialogU8(&path, filter_list.data(), (nfdfiltersize_t)filter_list.size(), nullptr, name.c_str());
    if (NFD_OKAY == res)
    {
        strPath = path;
        NFD_FreePathU8(path);
    }

    FiltersFree(filter_list);

    return strPath;
}

std::string PickFolder()
{
    std::string strPath;

    nfdu8char_t *path = nullptr;
    nfdresult_t res = NFD_PickFolderU8(&path, nullptr);
    if (NFD_OKAY == res)
    {
        strPath = path;
        NFD_FreePathU8(path);
    }

    return strPath;
}

void ShowLogFileContent()
{
    DefaultAppOpen(log_file_path());
}
